/**
 * Copyright 2019 jianggujin (www.jianggujin.com).
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.jianggujin.fos.obs;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import com.jianggujin.fos.JBucket;
import com.jianggujin.fos.JClient;
import com.jianggujin.fos.JClientWrapper;
import com.jianggujin.fos.JFOSException;
import com.jianggujin.fos.JListObjectsRequest;
import com.jianggujin.fos.JObject;
import com.jianggujin.fos.JObjectListing;
import com.jianggujin.fos.JObjectMetadata;
import com.jianggujin.fos.JObjectSummary;
import com.jianggujin.fos.obs.JOBSConfiguration.JOBSClientConfiguration;
import com.jianggujin.fos.obs.JOBSConfiguration.JOBSSessionTokenConfiguration;
import com.jianggujin.fos.util.JObjectUtils;
import com.obs.services.BasicObsCredentialsProvider;
import com.obs.services.ObsClient;
import com.obs.services.ObsConfiguration;
import com.obs.services.model.DeleteObjectsRequest;
import com.obs.services.model.DeleteObjectsResult;
import com.obs.services.model.DeleteObjectsResult.ErrorResult;
import com.obs.services.model.HttpMethodEnum;
import com.obs.services.model.ListBucketsRequest;
import com.obs.services.model.ListObjectsRequest;
import com.obs.services.model.ObjectMetadata;
import com.obs.services.model.ObsBucket;
import com.obs.services.model.ObsObject;
import com.obs.services.model.TemporarySignatureRequest;

import lombok.Getter;
import lombok.NonNull;

/**
 * 华为云客户端
 * 
 * @author jianggujin
 *
 */
@Getter
public class JOBSClient implements JClient {
    private JOBSConfiguration configuration;
    private JClientWrapper<ObsClient> wrapper;

    public JOBSClient(@NonNull JOBSConfiguration configuration) {
        this.configuration = configuration;
        ObsConfiguration config = new ObsConfiguration();
        JOBSClientConfiguration clientConfiguration = configuration.getClientConfiguration();
        if (clientConfiguration != null) {
            JObjectUtils.copyProperties(configuration, config, true);
        }
        config.setEndPoint(configuration.getEndPoint());

        JOBSSessionTokenConfiguration sessionTokenConfiguration = configuration.getSessionTokenConfiguration();
        if (sessionTokenConfiguration != null) {
            String securityToken = sessionTokenConfiguration.getSecurityToken();
            // 已有securityToken，直接初始化
            if (securityToken != null) {
                this.wrapper = new JOBSClientWrapper(new BasicObsCredentialsProvider(configuration.getAccessKey(),
                        configuration.getSecretKey(), securityToken), config);
                return;
            }
            // 自动获取sessionToken
            this.wrapper = new JOBSSecurityClientWrapper(config, new JOBSSecurityTokenService(configuration));
            return;
        }

        this.wrapper = new JOBSClientWrapper(
                new BasicObsCredentialsProvider(configuration.getAccessKey(), configuration.getSecretKey()), config);
    }

    @Override
    public List<JBucket> listBuckets() throws JFOSException {
        // 列举桶
        ListBucketsRequest request = new ListBucketsRequest();
        request.setQueryLocation(true);
        try {
            List<ObsBucket> buckets = this.wrapper.getClient().listBuckets(request);
            List<JBucket> jBuckets = new ArrayList<JBucket>(buckets.size());
            for (ObsBucket bucket : buckets) {
                jBuckets.add(new JOBSBucket(bucket));
            }
            return jBuckets;
        } catch (Exception e) {
            throw new JFOSException(e.getMessage(), e);
        }
    }

    @Override
    public JBucket createBucket(String bucketName) throws JFOSException {
        return new JOBSBucket(this.wrapper.getClient().createBucket(bucketName));
    }

    @Override
    public void deleteBucket(String bucketName) throws JFOSException {
        this.wrapper.getClient().deleteBucket(bucketName);
    }

    @Override
    public boolean doesBucketExist(String bucketName) throws JFOSException {
        return this.wrapper.getClient().headBucket(bucketName);
    }

    @Override
    public JObjectListing listObjects(String bucketName) throws JFOSException {
        return new JOBSObjectListing(this.wrapper.getClient().listObjects(
                new ListObjectsRequest(bucketName, null, null, null, JListObjectsRequest.DEFAULT_RETURNED_KEYS_LIMIT)));
    }

    @Override
    public JObjectListing listObjects(String bucketName, String prefix) throws JFOSException {
        return new JOBSObjectListing(this.wrapper.getClient().listObjects(new ListObjectsRequest(bucketName, prefix,
                null, null, JListObjectsRequest.DEFAULT_RETURNED_KEYS_LIMIT)));
    }

    @Override
    public JObjectListing listObjects(JListObjectsRequest request) throws JFOSException {
        return new JOBSObjectListing(
                this.wrapper.getClient().listObjects(new ListObjectsRequest(request.getBucketName(),
                        request.getPrefix(), request.getMarker(), request.getDelimiter(), request.getMaxKeys())));
    }

    @Override
    public JObjectMetadata getObjectMetadata(String bucketName, String key) throws JFOSException {
        return new JOBSObjectMetadata(this.wrapper.getClient().getObjectMetadata(bucketName, key));
    }

    @Override
    public void deleteObject(JObjectSummary summary) throws JFOSException {
        this.wrapper.getClient().deleteObject(summary.getBucketName(), summary.getKey());
    }

    @Override
    public void deleteObject(String bucketName, String key) throws JFOSException {
        this.wrapper.getClient().deleteObject(bucketName, key);
    }

    @Override
    public List<String> deleteObjects(String bucketName, List<String> keys) throws JFOSException {
        DeleteObjectsRequest deleteRequest = new DeleteObjectsRequest(bucketName);
        deleteRequest.setQuiet(true);
        for (String key : keys) {
            deleteRequest.addKeyAndVersion(key);
        }
        DeleteObjectsResult deleteResult = this.wrapper.getClient().deleteObjects(deleteRequest);
        List<ErrorResult> errorResults = deleteResult.getErrorResults();
        if (errorResults == null) {
            return Collections.emptyList();
        }
        List<String> result = new ArrayList<String>(errorResults.size());
        for (ErrorResult errorResult : errorResults) {
            result.add(errorResult.getObjectKey());
        }
        return result;
    }

    @Override
    public void copyObject(String sourceBucketName, String sourceKey, String destinationBucketName,
            String destinationKey) throws JFOSException {
        this.wrapper.getClient().copyObject(sourceBucketName, sourceKey, destinationBucketName, destinationKey);
    }

    @Override
    public JObject getObject(String bucketName, String key) throws JFOSException {
        return new JOBSObject(this.wrapper.getClient().getObject(bucketName, key));
    }

    @Override
    public JObjectMetadata getObject(String bucketName, String key, File file) throws JFOSException {
        ObsObject object = this.wrapper.getClient().getObject(bucketName, key);
        try (FileOutputStream out = new FileOutputStream(file)) {
            byte[] buffer = new byte[512];
            int len = -1;
            while ((object.getObjectContent().read(buffer)) != -1) {
                out.write(buffer, 0, len);
            }
            out.flush();
        } catch (Exception e) {
            throw new JFOSException(e.getMessage(), e);
        }
        return new JOBSObjectMetadata(object.getMetadata());
    }

    @Override
    public void putObject(String bucketName, String key, InputStream input) throws JFOSException {
        this.wrapper.getClient().putObject(bucketName, key, input);
    }

    @Override
    public void putObject(String bucketName, String key, InputStream input, String contentType) throws JFOSException {
        ObjectMetadata objectMetadata = new ObjectMetadata();
        if (contentType != null) {
            objectMetadata.setContentType(contentType);
        }
        this.wrapper.getClient().putObject(bucketName, key, input, objectMetadata);
    }

    @Override
    public void putObject(String bucketName, String key, File file) throws JFOSException {
        this.wrapper.getClient().putObject(bucketName, key, file);
    }

    @Override
    public void putObject(String bucketName, String key, File file, String contentType) throws JFOSException {
        ObjectMetadata objectMetadata = new ObjectMetadata();
        if (contentType != null) {
            objectMetadata.setContentType(contentType);
        }
        this.wrapper.getClient().putObject(bucketName, key, file, objectMetadata);
    }

    @Override
    public String generateUrl(String bucketName, String key, int expires) throws JFOSException {
        TemporarySignatureRequest request = new TemporarySignatureRequest(HttpMethodEnum.GET, expires);
        request.setBucketName(bucketName);
        request.setObjectKey(key);
        return this.wrapper.getClient().createTemporarySignature(request).getSignedUrl();
    }

    @Override
    public void destory() {
        if (this.wrapper != null) {
            this.wrapper.destory();
        }
    }
}
